// Class definitions for an Adjacency Matrix Graph

#include "MatrixGraph.h"

vector < Vertex * > * MatrixGraph::getAllVertices() {

  vector < Vertex * > * toRet = new vector< Vertex *>;
  for (auto it = vertices->begin(); it != vertices->end(); it ++) {
    toRet->push_back(it->second);

  }
  return toRet;

}

vector < Edge * > * MatrixGraph::getAllEdges() {

  vector < Edge * > * toRet = new vector < Edge * >;
  for (auto row = usedEntries->begin(); row != usedEntries->end(); row ++) {
    for (auto col = usedEntries->begin(); col != usedEntries->end(); col++) {
      if (adjMatrix[*row][*col] != NULL) {
        toRet->push_back(adjMatrix[*row][*col]);
      }
    }
  }
  return toRet;
}

Edge * MatrixGraph::getEdge(string src, string dest) {
  int s = checkVertex(src);
  int d = checkVertex(dest);
  return adjMatrix[s][d];
}


int MatrixGraph::containsVertex( string ID ) {
  return checkVertex(ID) > -1;
}

int MatrixGraph::size() {
  return vertices->size();
}

int MatrixGraph::consistencyCheck() {

  cout << "\n\nGraph Status: " << endl;
  assert(freeEntries->size() + usedEntries->size() == MAXVERTICES);
  cout << "Free and used entries properly accounted for. " << endl;
  assert(vertices->size() == usedEntries->size());
  cout << "Used vertex list matches expected size. " << endl;
  
  cout << "\nPrinting Graph: " << endl << endl;
  for (auto it = vertices->begin(); it != vertices->end(); it ++) {
    cout << "Vertex at Index " << it->second->idx << " (" << it->first << "): ";
    for ( int i = 0; i < MAXVERTICES; i++) {
      if (adjMatrix[it->second->idx][i] != NULL) {
	cout << adjMatrix[it->second->idx][i]->dest->label << ", ";
      }
    }
    cout << endl;
  }
  return 0;
}



int MatrixGraph::checkVertex(string v) {
  if (vertices->find(v) != vertices->end()) {
    return vertices->find(v)->second->idx;
  }
  return -1;
}

Vertex * MatrixGraph::getVertex(string v) {
  if (vertices->find(v) != vertices->end()) {
    return vertices->find(v)->second;
  }
  return NULL;
}
  
MatrixGraph::MatrixGraph() { 
  int i, j;
  int max = MAXVERTICES;
  for (i = 0; i< max; i++) {
    for (j = 0; j < max; j++ ) {
      adjMatrix[i][j] = NULL;
    }
  }
  freeEntries = new unordered_set<int>;
  usedEntries = new unordered_set<int>;
  for (i = 0; i < max; i++) {
    freeEntries->insert(i);
  }

  vertices = new unordered_map<string, Vertex *>;

}

MatrixGraph::~MatrixGraph() { 
  
  for ( auto it1 = usedEntries->begin(); it1 != usedEntries->end(); it1++ ) {
    for (auto it2 = usedEntries->begin() ; it2 != usedEntries->end(); it2++ ) {
      if ( adjMatrix[*it1][*it2] ) {
	delete ( adjMatrix[*it1][*it2] );
      }
    }
  }
  for ( auto it = vertices->begin(); it != vertices->end(); it++ ) {
    delete it->second;
  }
  delete vertices;
  delete freeEntries;
  delete usedEntries;

}
  

int MatrixGraph::containsEdge( string src, string dest ) {

  int s = checkVertex(src);
  int d = checkVertex(dest);
  if (s > -1 and d > -1) {
  
    return (adjMatrix[s][d] != NULL);
  }
  return 0;
}


void MatrixGraph::insertEdge( string src, string dest, int weight, string label ) {
  
  Vertex * s = getVertex(src);
  if ( ! s ) {
    s = insertVertex(src);
  }
  Vertex * d = getVertex(dest);
  if ( ! d ) {
    d = insertVertex(dest);
  }
  if (s and d ) {
    if ( adjMatrix[s->idx][d->idx] == NULL ) {
      Edge * e = new Edge(s, d, weight, label);
      adjMatrix[s->idx][d->idx] = e;
    }
    else {
      adjMatrix[s->idx][d->idx]->weight = weight;
      adjMatrix[s->idx][d->idx]->label = label;
    }
  }
}

void MatrixGraph::removeEdge( string src, string dest ) {

  int s = checkVertex(src);
  int d = checkVertex(dest);
  if (s > -1 and d > -1) {
    if ( adjMatrix[s][d] != NULL ) {
      delete adjMatrix[s][d];
      adjMatrix[s][d] = NULL;
    }
  }
}

  
Vertex * MatrixGraph::insertVertex( string label ) {
  
  if ( checkVertex(label) > -1 ) {
    return getVertex(label);
  }

  Vertex * v  = new Vertex(label);
  int ID = *freeEntries->erase(freeEntries->begin());
  v->idx = ID;
  usedEntries->insert( ID );
  vertices->insert(make_pair(label, v));
  return v;

}

void MatrixGraph::removeVertex( string v ) {
  int s = checkVertex( v );
  if ( s > -1 ) {
    for (int i = 0; i< MAXVERTICES; i++) {
      if ( adjMatrix[s][i] != NULL ) {
	delete adjMatrix[s][i];
	adjMatrix[s][i] = NULL;
      }
    }
    for (int i = 0; i < MAXVERTICES; i++) {
      if (adjMatrix[i][s] != NULL) {
	delete adjMatrix[i][s];
	adjMatrix[i][s] = NULL;
      }
    }
    delete vertices->find(v)->second;
    vertices->erase( v );
    freeEntries->insert(s);
    usedEntries->erase(s);
  }
}



vector < Vertex * > * MatrixGraph::getNeighbors( string src ) {


  int s = checkVertex( src );
  if ( s > -1 ) {
    vector < Vertex * > * toReturn = new vector< Vertex *>;
    for (int i = 0; i<MAXVERTICES; i++) {
      if ( adjMatrix[s][i] != NULL ) {
	toReturn->push_back(adjMatrix[s][i]->dest);
      }
    }
    return toReturn;
  }
  return NULL;
  

}

vector < Edge * > * MatrixGraph::getOutgoingEdges( string src ) {

  int s = checkVertex( src );
  if ( s > -1 ) {
    vector < Edge * > * toReturn = new vector< Edge *>;
    for (int i = 0; i<MAXVERTICES; i++) {
      if ( adjMatrix[s][i] != NULL ) {
	toReturn->push_back(adjMatrix[s][i]);
      }
    }
    return toReturn;
  }
  return NULL;

}

vector < Edge * > * MatrixGraph::getIncomingEdges( string src ) {

  int s = checkVertex( src );
  if ( s > -1 ) {
    vector < Edge * > * toReturn = new vector< Edge *>;
    for (int i = 0; i<MAXVERTICES; i++) {
      if ( adjMatrix[i][s] != NULL ) {
	toReturn->push_back(adjMatrix[i][s]);
      }
    }
    return toReturn;
  }
  return NULL;


}




  
